XML Files

WSDL, Web Services, and More

Aaron Skonnard

Code download available at:XMLFiles0212.exe(78 KB)

Contents

Invalid WSDL Document
Import Problems
Side Effects of "Parameters"

Q Are there any Web Services Description Language (WSDL) editors available today?

Q Are there any Web Services Description Language (WSDL) editors available today?

A Yes (and you have no idea how happy it makes me to be able to answer in the affirmative). WSDL editors are finally becoming available, a fact that greatly simplifies the task of defining Web Service interfaces. Up to this point, WSDL's sheer complexity has made authoring WSDL files nothing more than a great punishment for misbehaving developers. The WSDL editors I'll discuss here have been able to change that by hiding much of the needless complexity.

A Yes (and you have no idea how happy it makes me to be able to answer in the affirmative). WSDL editors are finally becoming available, a fact that greatly simplifies the task of defining Web Service interfaces. Up to this point, WSDL's sheer complexity has made authoring WSDL files nothing more than a great punishment for misbehaving developers. The WSDL editors I'll discuss here have been able to change that by hiding much of the needless complexity.

Figure 1 WSDL Editor in XML Spy

Figure 1** WSDL Editor in XML Spy **

Most XML developers will be happy to hear that Altova's XML Spy includes WSDL editing in version 5.0. A beta is currently available from https://www.altova.com for those who have permanent 4.0 licenses (see Figure 1). OmniOpera (https://www.omniopera.com) is another commercial WSDL editor that comes with some intuitive WSDL wizards to get you up and running fast. And finally, Cape Clear recently released a free WSDL editor (see Figure 2) that used to come as part of their complete CapeStudio framework (requires JDK 1.3.1).

Figure 2 Cape Clear's WSDL Editor

Figure 2** Cape Clear's WSDL Editor **

Q I've been struggling to get wsdl.exe to work properly. Are there any tricks that I need to be aware of? When I run it, I'm not getting the result I expect.

Q I've been struggling to get wsdl.exe to work properly. Are there any tricks that I need to be aware of? When I run it, I'm not getting the result I expect.

A As you've experienced, wsdl.exe is a sensitive piece of code that has to be handled just right for it to be happy (reminiscent of my spouse). And although it does a great job once you get to know it, it definitely has been the cause of some developer frustration. I'll outline the main issues for you. And, by the way, since the Add Web Reference dialog in Visual Studio® .NET seems to use the same underlying plumbing, most of what you read next applies to it as well (I'll point out any notable differences).

A As you've experienced, wsdl.exe is a sensitive piece of code that has to be handled just right for it to be happy (reminiscent of my spouse). And although it does a great job once you get to know it, it definitely has been the cause of some developer frustration. I'll outline the main issues for you. And, by the way, since the Add Web Reference dialog in Visual Studio® .NET seems to use the same underlying plumbing, most of what you read next applies to it as well (I'll point out any notable differences).

Wsdl.exe is a tool that comes with the Microsoft® .NET Framework SDK for generating .NET proxy and server code from WSDL definitions. When you run wsdl.exe and don't get the expected result, there are three likely problems: you have an invalid WSDL document, there are problems related to imports, or there are side effects from using the special part name "parameters." Let's take a look at each type of problem and discuss the possible workarounds.

Invalid WSDL Document

Due to the sheer complexity of WSDL, the invalid document is probably the most common problem when developers get errors running wsdl.exe. This means that there is a bug somewhere in your WSDL document that needs to be fixed. It's hard to blame the tool when you've given it a faulty WSDL document, but the error messages could be more helpful in certain cases.

So if you're not getting a helpful message from wsdl.exe, your best bet is to try one of several other WSDL validators available on the Web or one of the WSDL editors mentioned in the previous question. The error messages produced by these tools may point you in the right direction if wsdl.exe doesn't. If further tests don't indicate that there's something wrong with the WSDL file, continue reading.

Import Problems

A well-designed WSDL definition is typically built from various individual documents. This allows the author to factor the overall definition into different levels of abstraction that are easier to reuse and maintain over time. It's quite common to put the XML Schema type definitions in one file (see Figure 3), the WSDL abstract definitions (messages and portTypes) in another file (see Figure 4), and the WSDL concrete binding information (bindings and services) in yet another file (see Figure 5), all linked together through the WSDL import element.

Figure 5 Math WSDL Service

<wsdl:definitions name="MathService" targetNamespace="https://example.org/math/wsdl/service" xmlns:tns="https://example.org/math/wsdl/service" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:soap="https://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/" xmlns:itf="https://example.org/math/wsdl/interface" > <wsdl:import location="math-interface.wsdl" namespace="https://example.org/math/wsdl/interface"/> <wsdl:binding name="MathServiceSoapBinding" type="itf:IMath"> <soap:binding style="document" transport="https://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="Divide"> <soap:operation style="document" soapAction="https://example.org/math/Divide"/> <wsdl:input name="DivideInput"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="DivideOutput"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="MathService"> <wsdl:port name="MathServiceSoapPort" binding="tns:MathServiceSoapBinding"> <soap:address location="https://localhost/mathservice/math.ashx"/> </wsdl:port> </wsdl:service> </wsdl:definitions>

Figure 4 Math WSDL Interface

<wsdl:definitions name="MathService" targetNamespace="https://example.org/math/wsdl/interface" xmlns:tns="https://example.org/math/wsdl/interface" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/" xmlns:types="https://example.org/math/types" > <wsdl:import location="math.xsd" namespace="https://example.org/math/types"/> <wsdl:message name="DivideSoapOutMsg"> <wsdl:part name="parameters" element="types:DivideResponse"/> </wsdl:message> <wsdl:message name="DivideSoapInMsg"> <wsdl:part name="parameters" element="types:Divide"/> </wsdl:message> <wsdl:portType name="IMath"> <wsdl:operation name="Divide"> <wsdl:input name="DivideInput" message="tns:DivideSoapInMsg"/> <wsdl:output name="DivideOutput" message="tns:DivideSoapOutMsg"/> </wsdl:operation> </wsdl:portType> </wsdl:definitions>

Figure 3 Math XML Schema Definition

<xs:schema targetNamespace="https://example.org/math/types" xmlns:tns="https://example.org/math/types" xmlns:xs="https://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified" attributeFormDefault="unqualified" > <xs:complexType name="MathRequest"> <xs:sequence> <xs:element name="operand1" type="xs:double"/> <xs:element name="operand2" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="MathResponse"> <xs:sequence> <xs:element name="result" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:element name="Divide" type="tns:MathRequest"/> <xs:element name="DivideResponse" type="tns:MathResponse"/> </xs:schema>

WSDL relies on the semantics defined by XML Schema for imports, which states that the targetNamespace of the imported document must be different from that of the importing document (see Figure 3, Figure 4, and Figure 5 for examples). Failing to adhere to this rule can be the source of strange wsdl.exe errors. Wsdl.exe actually has no problem if the targetNamespace of a separate XML Schema file is the same as that of an importing WSDL file (although it's hard to tell whether this violates WSDL's rules for import), but it definitely complains if two WSDL files share the same targetNamespace when importing one into the other.

The major issue relating to imports, however, has to do with how wsdl.exe resolves the import locations. If you provide an absolute URI, the tool works fine (such as https://develop.com/math.wsdl). However, if you're trying to process a local file, a full file path (c:\temp\math.wsdl) won't work. Instead, you need to supply an absolute file URI (file://c:/temp/math. wsdl). If you don't provide an absolute URI, wsdl.exe always has problems resolving the imports.

This is one difference between wsdl.exe and the Visual Studio .NET Add Web Reference dialog. The dialog resolves imports just fine even when you only supply an absolute file path and not a complete URI.

Side Effects of "Parameters"

Both wsdl.exe and the WebMethod infrastructure exercise special behavior when a special part name, "parameters," is used in a WSDL definition. The exact behavior is undocumented, but it's critical to understand the convention if you happen to use it. First, assume the schema definition in Figure 6 that contains request/response message types. Then assume these types are referenced from some WSDL message constructs, as shown here:

<wsdl:message name="DivideSoapInMsg"> <wsdl:part name="data" element="types:Divide"/> </wsdl:message> <wsdl:message name="DivideSoapOutMsg"> <wsdl:part name="data" element="tns:DivideResponse"/> </wsdl:message>

And finally, here's the portType definition that combines the messages into an operation:

<wsdl:portType name="IMath"> <wsdl:operation name="Divide"> <wsdl:input name="DivideInput" message="tns:DivideSoapInMsg"/> <wsdl:output name="DivideOutput" message="tns:DivideSoapOutMsg"/> </wsdl:operation> </wsdl:portType>

Figure 6 Schema Definition

<xs:complexType name="MathRequest"> <xs:sequence> <xs:element name="operand1" type="xs:double"/> <xs:element name="operand2" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:complexType name="MathResponse"> <xs:sequence> <xs:element name="result" type="xs:double"/> </xs:sequence> </xs:complexType> <xs:element name="Divide" type="tns:MathRequest"/> <xs:element name="DivideResponse" type="tns:MathResponse"/>

Assuming a document/literal binding for the complete definition, wsdl.exe will produce a proxy method for the Divide operation similar to this (attributes omitted for clarity):

public MathResponse Divide(MathRequest Divide1) { // implementation omitted } public class MathRequest { public double operand1; public double operand2; } public class MathResponse { public double result; }

This makes sense from the XML Schema/WSDL perspective. However, if you change the name of the message parts to "parameters" instead of "data," wsdl.exe will then produce the following proxy method instead (again, attributes omitted for clarity):

public double Divide(double operand1, double operand2) { // implementation omitted }

Notice that if you use name="parameters" when defining the message parts, wsdl.exe expects to find an element representing the operation (in this case, Divide), which contains child elements (or attributes) representing the "parameters" list. It also expects to find an element representing the return value (in this case, DivideResponse), which contains a child element (or attribute) representing the return type.

Both versions of the proxy method produce the same XML message on the wire; the difference is in the mapping back to the code. In general, you shouldn't have to worry about such details, but in this case the "parameters" convention can trip you up. For example, if you change the type of the DivideResponse element to xs:double (instead of tns:MathResponse), which seems reasonable if you're only returning a simple type, wsdl.exe will fail since the "parameters" convention clearly doesn't work.

In general, it's useful to understand this convention since it's the default behavior used by ASP.NET (on the server side) when generating WSDL for WebMethods. If you create a simple ASP.NET Web Service and browse to the WSDL file, you'll find "parameters" used in all the part names. You can turn off the "parameters" convention by using the SoapParameterStyle.Bare setting on a WebMethod, as shown here:

[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]

If you're building Web Services in ASP.NET and building proxies using wsdl.exe, you shouldn't run into this problem since both sides were built around the same convention. You may run into the problem, though, when you're running wsdl.exe on files produced elsewhere, especially those produced by hand.

In addition to the issues I just discussed, there are several advanced areas of XML Schema and WSDL that aren't fully supported by the framework tools (xsd.exe and wsdl.exe). Examples include derivation by restriction, facets, mixed content models, and substitution groups, among others. In general, the framework tools shouldn't choke on these, just ignore them. I've provided several sets of sample WSDL documents that you can download from the link at the top of this article.

Q I'm writing a Web Service handler using the XML and HTTP APIs directly. What do I need to do to make sure my implementation works with a wsdl.exe-generated proxy?

Q I'm writing a Web Service handler using the XML and HTTP APIs directly. What do I need to do to make sure my implementation works with a wsdl.exe-generated proxy?

A First, you need to make sure that you have all of the HTTP settings right, as defined by the SOAP specification. For example, you need to make sure that your IHttpHandler implementation only accepts HTTP POST requests of Content-Type text/xml:

if (ctx.Request.HttpMethod != "POST" || !ctx.Request.ContentType.StartsWith("text/xml")) throw new Exception("unsupported method/Content-Type");

More importantly, you need to make sure that the Content-Type of the response is also text/xml, for both success and failure:

ctx.Response.ContentType = "text/xml";

In the case of failure, you also need to set the HTTP status code to 500 and the status description to "Internal Server Error" before writing the SOAP Fault back to the client, as shown in Figure 7.

A First, you need to make sure that you have all of the HTTP settings right, as defined by the SOAP specification. For example, you need to make sure that your IHttpHandler implementation only accepts HTTP POST requests of Content-Type text/xml:

if (ctx.Request.HttpMethod != "POST" || !ctx.Request.ContentType.StartsWith("text/xml")) throw new Exception("unsupported method/Content-Type");

More importantly, you need to make sure that the Content-Type of the response is also text/xml, for both success and failure:

ctx.Response.ContentType = "text/xml";

In the case of failure, you also need to set the HTTP status code to 500 and the status description to "Internal Server Error" before writing the SOAP Fault back to the client, as shown in Figure 7.

Figure 7 HTTP Status Code and Description Settings

ctx.Response.StatusCode = 500; ctx.Response.StatusDescription = "Internal Server Error"; tw.WriteStartDocument(); tw.WriteStartElement("s", "Envelope", SOAPNS); tw.WriteStartElement("s", "Body", SOAPNS); tw.WriteStartElement("s", "Fault", SOAPNS); tw.WriteElementString("faultcode", "s:Client"); tw.WriteElementString("faultstring", e.Message); tw.WriteElementString("detail", e.StackTrace); tw.WriteEndElement(); //Fault tw.WriteEndElement(); //Body tw.WriteEndElement(); //Envelope tw.WriteEndDocument();

As long as you get this stuff right, the proxy shouldn't have problems communicating with your implementation, and the returned SOAP Faults should automatically map back to .NET exceptions.

In addition to the HTTP details, you need to make sure that you write your XML processing code according to the XML Schema definition and WSDL documents that were used to generate the proxy, paying special attention to namespace specifics (qualified versus unqualified local elements). For example, Figure 8 shows the correct processing code for the Divide operation that was shown in the WSDL files in Figure 3, Figure 4, and Figure 5.

Figure 8 Divide Operation

const string SOAPNS = "https://schemas.xmlsoap.org/soap/envelope/"; const string MATHNS = "https://example.org/math/types"; XmlTextReader tr=new XmlTextReader(ctx.Request.InputStream); tr.ReadStartElement("Envelope", SOAPNS); // (omitted) deal with mustUnderstand headers here tr.ReadStartElement("Body", SOAPNS); tr.ReadStartElement("Divide", MATHNS); // these elements are in no namespace double operand1 = XmlConvert.ToDouble(tr.ReadElementString("operand1")); double operand2 = XmlConvert.ToDouble(tr.ReadElementString("operand2")); tr.ReadEndElement(); //Divide tr.ReadEndElement(); //Body tr.ReadEndElement(); //Envelope tr.Close();

And the correct code for generating the Divide response message is as follows:

tw.WriteStartDocument(); tw.WriteStartElement("s", "Envelope", SOAPNS); tw.WriteStartElement("s", "Body", SOAPNS); tw.WriteStartElement("m", "DivideResponse", MATHNS); // result element is in no namespace tw.WriteElementString("result", XmlConvert.ToString(result)); tw.WriteEndElement(); //DivideResponse tw.WriteEndElement(); //Body tw.WriteEndElement(); //Envelope tw.WriteEndDocument();

It's also worth noting that there was a bug in the v1 release related to chunked responses. In certain cases, the HTTP pipeline would generate a "bad" chunked stream that would cause clients to fail. If you don't have the latest service pack installed, make sure you call Response.End before returning or closing the stream, which seems to solve the problem.

The complete sample along with a wsdl.exe-generated proxy that invokes the service is available for download from the MSDN Magazine Web site. You can test both success and failure to see the SOAP Faults being mapped back to .NET exceptions.

Q How do you inspect the wire-level messages when invoking a Web Service?

Q How do you inspect the wire-level messages when invoking a Web Service?

A The easiest way to do this, regardless of the toolkit you work with, is to use a local trace utility. There are several readily available, including the Microsoft® SOAP Trace Utility (MSSoapT) that comes with the SOAP Toolkit as well as Simon Fell's excellent tcpTrace utility (see https://www.pocketsoap.com). They both work the same way: start the trace utility and configure it to listen on a specific port (say 8080) and then forward all messages to the correct server and destination port (localhost:80 for your local HTTP server).

A The easiest way to do this, regardless of the toolkit you work with, is to use a local trace utility. There are several readily available, including the Microsoft® SOAP Trace Utility (MSSoapT) that comes with the SOAP Toolkit as well as Simon Fell's excellent tcpTrace utility (see https://www.pocketsoap.com). They both work the same way: start the trace utility and configure it to listen on a specific port (say 8080) and then forward all messages to the correct server and destination port (localhost:80 for your local HTTP server).

Then you just need to make sure you modify your code to issue requests to port 8080 (instead of 80) and the messages will be captured. You can do this from a wsdl.exe-generated proxy by updating the Url property before invoking an operation:

MathServer ms = new MathService(); ms.Url = "https://localhost:8080/mathservice/ math.asmx"; double result = ms.Divide(33, 3);

If you built your proxy to read the URL from the client's application configuration file (via wsdl.exe's /urlkey switch), you can simply update the value found in the configuration file.

Simon Fell provides a similar tool, called proxyTrace, that acts as a local proxy server for capturing all traffic. You set it up the same way and then specify the proxy server in your code. You can also redirect your machine-wide proxy server (in the Internet Options control panel applet) to proxyTrace. This takes effect for every piece of code that reads from those settings (such as Microsoft Internet Explorer, MSXML, and so on) and makes it possible to capture traffic without modifying your code.

Figure 9 Request/Response in MSSoapT

Figure 9** Request/Response in MSSoapT **

One of the nice things about the Microsoft SOAP Trace Utility is that it enables you to render the request/response messages as formatted XML documents (see Figure 9). It also provides a binary view of the transmission.

Figure 10 Request/Response in tcpTrace

Figure 10** Request/Response in tcpTrace **

A nice text view of the HTTP message is provided by tcpTrace (see Figure 10), and it's much easier to read than a raw binary message. At least one of these utilities should be in every Web Service developer's toolbox. In many situations, the only way to troubleshoot a Web Service is by inspecting the wire message and comparing it to the exposed WSDL document.

Q I can access my Web Service fine from within Internet Explorer, but when I try to access it from a .NET client application, the server returns a 401 (not authorized) error message. Why is that?

Q I can access my Web Service fine from within Internet Explorer, but when I try to access it from a .NET client application, the server returns a 401 (not authorized) error message. Why is that?

A Whenever an HTTP request is issued against a vroot (virtual root) where anonymous access is disabled (and another form of authentication is enabled), the Web server will return a 401 status code (not authorized) along with an authentication challenge (through WWW-Authenticate headers). The server behaves the same regardless of where the request came from. The difference, however, has to do with how each client responds.

A Whenever an HTTP request is issued against a vroot (virtual root) where anonymous access is disabled (and another form of authentication is enabled), the Web server will return a 401 status code (not authorized) along with an authentication challenge (through WWW-Authenticate headers). The server behaves the same regardless of where the request came from. The difference, however, has to do with how each client responds.

Internet Explorer and various other Windows components (the XMLHttp class of MSXML, for example) use WinInet for their underlying HTTP communications. If you were to write the WinInet code directly, you would have to deal with the authentication challenge programmatically. Internet Explorer and those other Windows components instruct WinInet to respond to the authentication challenge by sending the system credentials of the current security context after the initial challenge. This makes it seem like it works magically (without authentication), but if you sniff the transmission using a trace utility (see the previous question), you'll see that multiple requests are indeed taking place and that credentials/tokens are being sent.

The .NET WinInet equivalent is found in the System.Net namespace. You use WebRequest to invoke an HTTP request. WSDL-based proxies are of type SoapHttpClientProtocol, which is basically just a wrapper around WebRequest. If you want either of these classes to respond to an authentication challenge, you must initialize the Credentials property with an ICredentials object. You can use CredentialCache.DefaultCredentials to send the system credentials for the current security context:

proxy.Credentials = CredentialCache.DefaultCredentials;

As long as you do this before calling GetResponse, WebRequest will automatically respond to the authentication challenge and give you the same functionality that you experienced with Internet Explorer.

If you don't want authentication at all, then there is an easier solution: simply enable anonymous access for the particular vroot using the administration console in Microsoft Internet Information Services (IIS). (Note that when you create a vroot through the "Web share" shortcut, anonymous access is disabled by default.)

Q What's the best model for securing a Web Service in .NET today?

Q What's the best model for securing a Web Service in .NET today?

A It depends on what you mean by securing. If you mean a complete security framework that includes authentication, integrity, and confidentiality, then Secure Sockets Layer (SSL) is the most complete, safe, and interoperable option today. Plus, it's pretty straightforward to get SSL up and running, especially if you already have a certificate installed on your Web server. If you don't have a certificate, first you'll need to obtain one from a certificate authority like VeriSign.

A It depends on what you mean by securing. If you mean a complete security framework that includes authentication, integrity, and confidentiality, then Secure Sockets Layer (SSL) is the most complete, safe, and interoperable option today. Plus, it's pretty straightforward to get SSL up and running, especially if you already have a certificate installed on your Web server. If you don't have a certificate, first you'll need to obtain one from a certificate authority like VeriSign.

Once you've obtained your certificate, install it in IIS (see the IIS administration console for more details). Then you'll be able to require secure communications on individual vroots. You can also require some form of authentication by disabling anonymous access. Basic authentication is fine in this scenario since you're already guaranteed to have a secure channel. By configuring your Web Service vroot in this fashion, you'll achieve authentication, integrity, and confidentiality without affecting a single line of your server-side code.

WS-Security is another security framework specification that coordinates the efforts of several other XML and security-related specifications. The fundamental difference between WS-Security and SSL is that the former works at the message level while the latter works at the channel level. WS-Security has incredible potential; unfortunately there isn't much code in place for it today (see the recently released Web Services Enhancements for a reference implementation). So unless you want to write a lot of the code yourself, you're probably better off waiting until WS-Security is more prevalent and interoperable across toolkits.

Q Does .NET support method overloading in a Web Service?

Q Does .NET support method overloading in a Web Service?

A WSDL 1.1 does not explicitly address operation overloading, which means it neither states how it should work nor does it disallow it. Method overloading is obviously supported in .NET, so there is nothing stopping you from overloading ASP.NET WebMethods at compile time. But if you do this and then try to access the .asmx endpoint, you'll get an exception saying that multiple operations use the same message name.

A WSDL 1.1 does not explicitly address operation overloading, which means it neither states how it should work nor does it disallow it. Method overloading is obviously supported in .NET, so there is nothing stopping you from overloading ASP.NET WebMethods at compile time. But if you do this and then try to access the .asmx endpoint, you'll get an exception saying that multiple operations use the same message name.

To get around this, you need to provide a message name alias, through WebMethod's MessageName property, for each overloaded operation. This will serve as the message name on the wire (instead of the operation name). For example, consider the following .asmx class:

public class Service : WebService { [WebMethod] public void DoSomething() { } [WebMethod(MessageName="DoSomethingWithParam")] public void DoSomething(string someParam) { } }

This translates to a WSDL portType that looks something like this:

<portType name="ServiceSoap"> <operation name="DoSomething"> <input message="s0:DoSomethingSoapIn" /> <output message="s0:DoSomethingSoapOut" /> </operation> <operation name="DoSomething"> <input name="DoSomethingWithParam" message="s0:DoSomethingWithParamSoapIn" /> <output name="DoSomethingWithParam" message="s0:DoSomethingWithParamSoapOut" /> </operation> </portType>

The operation names are the same for the overloaded methods, but the input/output message names are unique (and the underlying XML Schema types are different as well). This means that the messages on the wire are guaranteed to be completely different for each operation. So from an XML messaging perspective, there is really no overloading at all. The illusion of overloading is happening in the processing code. Wsdl.exe also happens to support mapping overloaded operations back to overloaded methods in the generated proxy class, but you shouldn't count on all WSDL toolkits supporting this.

There is one area of the WSDL spec that has been debated—the subject of whether to explicitly address operator overloading. According to the latest working draft, the committee is leaning towards completely disallowing it (see https://www.w3.org/TR/wsdl12/#names-of-elements-within-an-operation). Such a decision would make the previous WSDL fragment illegal. So if I were you, I wouldn't design my Web Services to take advantage of this technique even though it works today in .NET.

Send your questions and comments for Aaron to xml@microsoft.com.

Aaron Skonnardis an instructor/researcher at DevelopMentor, where he develops the XML and Web Service-related curriculum. Aaron coauthored Essential XML Quick Reference (Addison-Wesley, 2001) and Essential XML (Addison-Wesley, 2000).